using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using Nop.Services.Catalog;
using Nop.Core;
using Nop.Services.Media;
using WebGrease.Css.Extensions;
using Nop.Core.Domain.Catalog;
using Nop.Core.Domain.Customers;
using Nop.Core.ApiDataModel;
using Spotshub.Api.Filter;
using Nop.Services.Customers;
using System.Runtime.Caching;

namespace Spotshub.Api.Controllers.V1
{
    [ExceptionHandler]
    public class ProductController : ApiController
    {
        public class SearchProductModel
        {
            public string Keyword { get; set; }

            public int CategoryId { get; set; }

            public int SortBy { get; set; }  //0:default  5:NameAsc  6:NameDesc  10:PriceAsc 11:PriceDesc 15: CreatedOn  datetime

            public int PageIndex { get; set; }

            public int ItemCountPerPage { get; set; }

            public bool? IsFeaturedProduct { get; set; }

            public float? MinPrice { get; set; }

            public float? MaxPrice { get; set; }

            public int? RegionId { get; set; }
        }

		private const string PRODUCTLIST_CACHE_KEY = "q_{0}-cat_{1}-sort_{2}-pi_{3}-ps_{4}-if_{5}-min_{6}-max_{7}-region_{8}";

        private IProductService _productService;
        private IPictureService _pictureService;
		private ICustomerService _customerService;
		MemoryCache _cache;
        IWorkContext _workContext;

        public ProductController(IProductService productService, IPictureService pictureService, 
			ICustomerService customerService,IWorkContext workContext, MemoryCache cache)
        {
            this._productService = productService;
            this._pictureService = pictureService;
			this._customerService = customerService;
            this._workContext = workContext;
			this._cache = cache;
        }

        // GET: api/Product/Get
        public IEnumerable<Nop.Core.ApiDataModel.Product> Get([FromUri] SearchProductModel search)
        {
			var key = string.Format(PRODUCTLIST_CACHE_KEY, search.Keyword, search.CategoryId, search.SortBy, 
				search.PageIndex, search.ItemCountPerPage, search.IsFeaturedProduct.HasValue ? search.IsFeaturedProduct.Value.ToString() : string.Empty, 
				search.MinPrice.HasValue ? search.MinPrice.Value : 0, search.MaxPrice.HasValue ? search.MaxPrice.Value : 99999,
				search.RegionId.HasValue ? search.RegionId.Value: 1 );

			if (_cache.Contains(key))
				return _cache.Get(key) as IEnumerable<Nop.Core.ApiDataModel.Product>;
			else 
			{
				var result = SearchProduct(search);
				_cache.Add(key, result, new CacheItemPolicy { AbsoluteExpiration = new DateTimeOffset(DateTime.Now.AddMinutes(20)) });
				return result;
			}
            
        }

        // GET: api/Product/Get/5
        public Nop.Core.ApiDataModel.Product Get(int id)
        {
            Nop.Core.Domain.Catalog.ProductVariant productVariant = _productService.GetProductVariantById(id);
            if (productVariant == null)
            {
                return null;
            }

            Nop.Core.Domain.Catalog.Product product = _productService.GetProductById(productVariant.ProductId);
            var pictureUrls = new List<String>();
            if (product.ProductPictures != null)
                product.ProductPictures.ForEach(pp => pictureUrls.Add(_pictureService.GetPictureUrl(pp.PictureId)));

			var isMember = GetMembeshipStatus();
			var prod = new Nop.Core.ApiDataModel.Product(product, productVariant, pictureUrls, isMember);
            prod.IsShowButton = IsShowProductButton(productVariant);
            return prod;
        }



        // GET: api/Product/GetRelatedProducts/5
        public IEnumerable<Nop.Core.ApiDataModel.Product> GetRelatedProducts(int id, int regionId = 0)
        {
            var userRegionId = _workContext.CurrentCustomer.IsGuest() ? regionId : _workContext.CurrentCustomer.Region.Id;
            var relatedProducts = _productService.GetRelatedProductsByProductId1(id, userRegionId);
            if (relatedProducts == null || !relatedProducts.Any())
            {
                return new List<Nop.Core.ApiDataModel.Product>();
            }

			var isMember = GetMembeshipStatus();
            var result = new List<Nop.Core.ApiDataModel.Product>();
            foreach (RelatedProduct relatedProd in relatedProducts)
            {
                Nop.Core.Domain.Catalog.Product product = _productService.GetProductById(relatedProd.ProductId2);
                var pictureUrls = new List<String>();
                if (product.ProductPictures != null)
                    product.ProductPictures.ForEach(pp => pictureUrls.Add(_pictureService.GetPictureUrl(pp.PictureId)));
                foreach (Nop.Core.Domain.Catalog.ProductVariant productVar in product.ProductVariants)
                {
					result.Add(new Nop.Core.ApiDataModel.Product(product, productVar, pictureUrls, isMember));
                }
            }
            return result;
        }

        // GET: api/Product/GetCrossSellProducts/5
        public IEnumerable<Nop.Core.ApiDataModel.Product> GetCrossSellProducts(int id, int regionId = 0)
        {
            IList<CrossSellProduct> crossSellProducts = _productService.GetCrossSellProductsByProductId1(id,
                _workContext.CurrentCustomer.IsGuest() ? regionId : _workContext.CurrentCustomer.Region.Id);
            if (crossSellProducts == null || !crossSellProducts.Any())
            {
                return new List<Nop.Core.ApiDataModel.Product>();
            }

			var isMember = GetMembeshipStatus();
            var result = new List<Nop.Core.ApiDataModel.Product>();
            foreach (CrossSellProduct crossSellProd in crossSellProducts)
            {
                Nop.Core.Domain.Catalog.Product product = _productService.GetProductById(crossSellProd.ProductId2);
                var pictureUrls = new List<String>();
                if (product.ProductPictures != null)
                    product.ProductPictures.ForEach(pp => pictureUrls.Add(_pictureService.GetPictureUrl(pp.PictureId)));
                foreach (Nop.Core.Domain.Catalog.ProductVariant productVar in product.ProductVariants)
                {
					result.Add(new Nop.Core.ApiDataModel.Product(product, productVar, pictureUrls, isMember));
                }
            }

            return result;
        }



        // GET: api/Product/GetAllProductsDisplayedOnHomePage?regionId={regionId}
        public IEnumerable<Nop.Core.ApiDataModel.Product> GetAllProductsDisplayedOnHomePage(int regionId)
        {
            IList<Nop.Core.Domain.Catalog.Product> allHomePageProducts = _productService.GetAllProductsDisplayedOnHomePage(regionId);
            if (allHomePageProducts == null)
            {
                return null;
            }

			var isMember = GetMembeshipStatus();
            var result = new List<Nop.Core.ApiDataModel.Product>();
            foreach (Nop.Core.Domain.Catalog.Product product in allHomePageProducts)
            {
                var pictureUrls = new List<String>();
                if (product.ProductPictures != null)
                    product.ProductPictures.ForEach(pp => pictureUrls.Add(_pictureService.GetPictureUrl(pp.PictureId)));
                foreach (Nop.Core.Domain.Catalog.ProductVariant productVar in _productService.GetProductVariantsByProductId(product.Id))
                {
					result.Add(new Nop.Core.ApiDataModel.Product(product, productVar, pictureUrls, isMember));
                }
            }

            return result;
        }

        private List<Nop.Core.ApiDataModel.Product> SearchProduct(SearchProductModel search)
        {
            var searchResult = new List<Nop.Core.ApiDataModel.Product>();
            IList<int> filterableSpecificationAttributeOptionIds = null;
            decimal? minPrice = search.MinPrice == null ? null : (decimal?)(search.MinPrice.Value);
            decimal? maxPrice = search.MaxPrice == null ? null : (decimal?)(search.MaxPrice.Value);

            List<Nop.Core.Domain.Catalog.Product> featuredProducts = _productService.SearchProducts(
                _workContext.CurrentCustomer.Region == null ? 0 : _workContext.CurrentCustomer.Region.Id,
                search.CategoryId,
                0,
                null,//search.IsFeaturedProduct,
                minPrice,
                maxPrice,
                0,
                search.Keyword,
                true,
                true,
                _workContext.WorkingLanguage.Id,
                new List<int>(),
                (Nop.Core.Domain.Catalog.ProductSortingEnum)search.SortBy,
                search.PageIndex,
                search.ItemCountPerPage <= 0 ? 10 : search.ItemCountPerPage,
                false,
                out filterableSpecificationAttributeOptionIds
                ).ToList();
            //featuredProducts = featuredProducts.FilterForCustomer(_workContext.CurrentCustomer).ToList();
			var isMember = GetMembeshipStatus();
            foreach (Nop.Core.Domain.Catalog.Product product in featuredProducts)
            {
                foreach (Nop.Core.Domain.Catalog.ProductVariant productVariant in
                    product.GetProductVariants(_workContext.CurrentCustomer.Region == null ? 0 : _workContext.CurrentCustomer.Region.Id))
                {
                    var pictureUrls = new List<String>();
                    if (product.ProductPictures.Any())
                    {
                        pictureUrls.Add(_pictureService.GetPictureUrl(product.ProductPictures.First().Picture));
                    }
                    else
                    {
                        pictureUrls.Add(_pictureService.GetPictureUrl(0));
                    }
					searchResult.Add(new Nop.Core.ApiDataModel.Product(product, productVariant, pictureUrls, isMember));
                }
            }

            //HACK: Fix Price Sort issue for API
            if (searchResult.Count > 0)
            {
                switch ((Nop.Core.Domain.Catalog.ProductSortingEnum)search.SortBy)
                {
                    case ProductSortingEnum.PriceAsc:
                        searchResult = searchResult.OrderBy(p => p.Price).ToList();
                        break;
                    case ProductSortingEnum.PriceDesc:
                        searchResult = searchResult.OrderByDescending(p => p.Price).ToList();
                        break;
                }
            }          

            return searchResult;
        }

		private bool GetMembeshipStatus()
		{
			var customer = _workContext.CurrentCustomer;
			var role = _customerService.GetAllCustomerRoles().FirstOrDefault(r => r.SystemName.Equals(SystemCustomerRoleNames.DreamFactoryUser));
			var isMember = customer.CustomerRoleMappings.Any(crm => crm.CustomerRoleId == role.Id);
			return isMember;
		}

        private static decimal? GetCustomerTierPrice(Nop.Core.Domain.Catalog.ProductVariant productVariant, Customer customer)
        {
            var userRoles = customer.CustomerRoleMappings.Select(m => m.CustomerRole);

            if (productVariant.TierPrices != null)
            {
                var tierPrices = productVariant.TierPrices.Where(tp => tp.CustomerRole.SystemName.Equals(SystemCustomerRoleNames.DreamFactoryUser) && tp.Quantity == 1)
                    .ToList().FilterForCustomer(customer);
                if (tierPrices.Any())
                {
                    var minPrice = tierPrices.Min(tp => tp.Price);
                    if (minPrice < productVariant.Price)
                    {
                        return minPrice;
                    }
                }
            }
            return null;
        }


        private bool IsShowProductButton(ProductVariant productVariant)
        {
            var product = productVariant.Product;
            if (product == null)
            {
                return false;
            }

            //deleted?
            if (product.Deleted || productVariant.Deleted)
            {
                return false;
            }
            if (!product.Published || !productVariant.Published)
            {
                return false;
            }
            if (productVariant.DisableBuyButton)
            {
                return false;
            }
            
            return true;
        }
    }
}
